package org.juiceshop.musi;


import java.util.List;
import java.util.Map;


public class Note extends MusicObject implements Comparable<Note> {

	public static final int DEFAULT_PITCH = 60; // Middle-C
	public static final int DEFAULT_DYNAMIC = 64;
	public static final double DEFAULT_VALUE = 1d;
	public static final double DEFAULT_ARTICULATION = 0.9d;
	public static final KeySignature DEFAULT_KEY = KeySignature.major(0);
		
	public static final String[] LETTERS_SHARP  = {"C","C#","D","D#","E","F","F#","G","G#","A","A#","B"};
	public static final String[] LETTERS_FLAT =   {"C","Db","D","Eb","E","F","Gb","G","Ab","A","Hb","B"};

	public static final int NEUTRAL = Integer.MIN_VALUE;
	
	private final NoteName _name;
	private final KeySignature _key;
	private final int _accidentials;
	private final double _value;
	private final int _dynamic;
	private final double _articulation;
	private final boolean _slur;
	


	public Note(	final int pitch,
					final KeySignature key,
					final int accidentials,
					final double value,
					final int dynamic,
					final double articulation,
					final boolean slur) {
		_originalPitch = pitch;
		_key = key;
		_accidentials = accidentials;
		_value = value;
		_dynamic = dynamic;
		_articulation = articulation;
		_slur = slur;
	}
	
				
	public int getPitch() {
		return _originalPitch+_accidentials;
	}
	
	public KeySignature getKey() {
		return _key;
	}
	

	public int getDynamic() {
		return _dynamic;
	}
	
	@Override
	public double getValue() {
		return _value;
	}
	
	
	public double getArticulation() {
		return _articulation;
	}
	
	public boolean isSlur() {
		return _slur;
	}
	
	
	public String getSymbol() {
		return getName("#","b");
	}
	
	public String getName() {
		return getName("sharp","flat");
	}
	
	private String getName(final String sharpSymbol,  final String flatSymbol) {
		if (isRest()) return "R";
		int absAccidentials = _accidentials;
		String symbol = sharpSymbol;
		if (_accidentials<0) {
			absAccidentials = -_accidentials;
			symbol = flatSymbol;
		}
		boolean flat = getKey().getMidiSymbol()<0;
		StringBuilder result = new StringBuilder(flat ? LETTERS_FLAT[_originalPitch%12] : LETTERS_SHARP[_originalPitch%12]);
		for (int i=0; i<absAccidentials; i++) {
			result.append(symbol);
		}
		return result.toString();
	}
	
	
	public double getDuration() {return _value * getArticulation(); }
	
	
	public boolean isRest() {
		return _originalPitch==NoteLetter.R.getPitch();
	}
	
			
	@Override
	public Note transpose (int steps) {
		return new Note(_originalPitch+steps,_accidentials,_value,_dynamic,_articulation,_slur);
	}
	
	/**
	 * Diatonic increase or decrease.
	 * Creates a new note that is given full tones higher or lower in
	 * the current scale.
	 * Returns mere copy if the current note is a rest.
	 * @param steps Number of full note values to increase or decrease.
	 * @return New <code>Note</code> instance.
	 */
	public Note scale (KeySignature key, int steps) {
		int scalesSteps = key.scale(_originalPitch, steps);
		return new Note(_originalPitch+scalesSteps,_accidentials,_value,_dynamic,_articulation,_slur);
	}
	
		
	/**
	 * Diatonic increase (prefixed <code>++</code>-Operator).
	 * @see #scale(int).
	 */
	public Note next() {
		return scale(+1);
	}

	
	/**
	 * Diatonic decrease (prefixed <code>--</code>-Operator).
	 * @see #scale(int).
	 */
	public Note previous() {
		return scale(-1);
	}

	/**
	 * Compare note value.
	 */
	@Override
	public int compareTo(Note other) {
		return this.getPitch() - other.getPitch();
	}
	
	
	/**
	 * Number of notes.
	 * Returns always 1.
	 */
	@Override
	public int size() { return 1; }
		
	@Override
	public String toString () {
		return "<"+getSymbol()+"("+getValue()+")>";
	}

	
}
